package control

import (
	"context"
	"dockerui/models"
	ut "dockerui/utils"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/registry"
	"github.com/docker/docker/client"
	"github.com/fatih/structs"
	"io"
	"os"
	"time"
)

func dockerImages(skip int, limit int, search string) ([]models.ImageFormatResp,
int, error) {
	var (
		ctx context.Context
		cancel context.CancelFunc
		cli *client.Client
		err error
		imageList *types.ImageListOptions
		images []types.ImageSummary
		image types.ImageSummary
		imagesResp []map[string]interface{}
		imageMap map[string]interface{}
		verifiedSkip int
		verifiedLimit int
		formatImageRes []models.ImageFormatResp
	)
	/*参考源码
	https://github.com/moby/moby/blob/master/integration-cli/docker_api_images_test.go
	*/
	// 带参数默认返回10
	if search != "" {
		imageFilters := filters.NewArgs()
		imageFilters.Add("reference", search)
		imageList = &types.ImageListOptions{
			All: false,
			Filters: imageFilters,
		}
	} else {
		imageList = &types.ImageListOptions{
			All: true,
		}
	}

	ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	cli = ut.GClient
	// 查看所有镜像
	if images, err = cli.ImageList(ctx, *imageList); err != nil {
		fmt.Println("err", err.Error())
		fmt.Println("images list err", err)
		return nil, len(imagesResp), err
	}
	for _, image = range images {
		imageMap = structs.Map(&image)
		imagesResp = append(imagesResp, imageMap)
		imagesResp = append(imagesResp, imageMap)
	}
	// bug ,查询容器会返回两个重复的,貌似是开源库问题
	formatImageRes = ut.FormatImageList(imagesResp)
	// @TODO 做好分页的验证
	verifiedSkip, verifiedLimit = ut.LimitVerify(skip, limit, len(formatImageRes))
	return formatImageRes[verifiedSkip:verifiedLimit], len(formatImageRes), nil
}


func searchImages(imageName string) ([]models.SearchImageFormatResp, int, error){
	/*
		从镜像仓库拉搜索镜像
	*/
	var (
		client *client.Client
		ctx context.Context
		cancel context.CancelFunc
		imageOpt types.ImageSearchOptions
		searchLimit int
		searchRes []registry.SearchResult
		imageSearch registry.SearchResult
		searchedName string
		searchedDescrp string
		searchedStars int
		searchedOfficial string
		searchMap map[string]interface{}
		searchImage models.SearchImageFormatResp  // 搜索的单个镜像
		searchMapRes []models.SearchImageFormatResp // 搜索的镜像返回值
		err error
	)
	client = ut.GClient

	ctx, cancel = context.WithTimeout(context.Background(), 10 * time.Second)
	defer cancel()

	searchLimit = 20

	imageOpt = types.ImageSearchOptions{
		Limit: searchLimit,
	}

	if searchRes, err = client.ImageSearch(ctx, imageName, imageOpt); err != nil {
		fmt.Println("search result", err)
		return nil, 0, err
	}
	for _, imageSearch = range searchRes {
		searchMap = structs.Map(imageSearch)
		searchedName = searchMap["Name"].(string)
		searchedDescrp = searchMap["Description"].(string)
		searchedStars = searchMap["StarCount"].(int)
		if searchMap["IsOfficial"].(bool) {
			searchedOfficial = "yes"
		} else {
			searchedOfficial = "no"
		}
		searchImage = models.SearchImageFormatResp{
			Name: searchedName,
			Description: searchedDescrp,
			StarsCount: searchedStars,
			IsOfficial: searchedOfficial,
		}
		searchMapRes = append(searchMapRes, searchImage)
	}
	return searchMapRes, len(searchMapRes), nil
}


func dockerImagePull(imageName string) {
	/*
		从镜像仓库拉取镜像
	*/
	var (
		authBase64 string
		authBytes []byte
		authType types.AuthConfig
		client *client.Client
		ctx context.Context
		cancel context.CancelFunc
		err error
		username string
		password string
		//repoUrl string
		imageReader io.ReadCloser
		imagePullOpt types.ImagePullOptions
		wr int64
	)

	username = "yourname"
	password = "yourpassword"
	authType = types.AuthConfig{
		Username: username,
		Password: password,
	}
	if authBytes, err = json.Marshal(authType); err != nil {
		return
		//return err
	}

	authBase64 = base64.URLEncoding.EncodeToString(authBytes)
	ctx, cancel = context.WithTimeout(context.Background(), 1000 *time.Second)
	defer cancel()

	client = ut.GClient
	imagePullOpt = types.ImagePullOptions{
		RegistryAuth: authBase64,
	}

	if imageReader, err = client.ImagePull(ctx, imageName, imagePullOpt); err != nil {
		fmt.Println("Pull ", imageName, " error", err)
		return
		//return err
	} else {
		// @TODO 此处应该用消息队列
		if wr, err = io.Copy(os.Stdout, imageReader); err != nil {
			fmt.Println("Copy ", imageName, " error", err)
			return
			//return err
		}
		ut.GDockerChain.DownloadProcessChan <- wr
		return
		//return nil
	}
}